#include "stdafx.h"
#include "FourierTransformFactoryD3DCSX.h"
#include "includeD3DCSX.h"
#include "fftutils.h"
#include <wrl\client.h>

using namespace FTLib;
using namespace Microsoft::WRL;

static ULONG GetRefCount(IUnknown* pUnk)
{
	pUnk->AddRef();
	ULONG ul = pUnk->Release();
	return ul;
}

class CD3DCSX11Base
{
protected:
	uint32_t width, height;
	ComPtr<ID3D11Device> pDevice;
	ComPtr<ID3D11DeviceContext> pDeviceContext;
	ComPtr<ID3DX11FFT> pFFT;
	ComPtr<ID3D11Buffer> inputBuffer;
	ComPtr<ID3D11UnorderedAccessView> inputUAV;
public:
	CD3DCSX11Base(uint32_t width, uint32_t height) : width(width), height(height)
	{}

	void Initialize(double normalizationFactor)
	{
		auto devAndDevContext = this->CreateDevice();
		this->pDevice = devAndDevContext.first;
		this->pDeviceContext = devAndDevContext.second;

		auto fftAndBufferInfo = this->CreateFFT(normalizationFactor);
		bool b = this->SetupBuffers(fftAndBufferInfo.first.Get(), fftAndBufferInfo.second);
		this->pFFT = fftAndBufferInfo.first;
		UINT numberOfFloatsForInputBuffer = this->CalcInputBufferSize(width, height);
		HRESULT hr = Create_Raw_Buffer(numberOfFloatsForInputBuffer, this->inputBuffer.GetAddressOf(), D3D11_CPU_ACCESS_WRITE);
		hr = CreateUnorderedAccessView(numberOfFloatsForInputBuffer, this->inputBuffer.Get(), this->inputUAV.GetAddressOf());
	}

	void Do_Transform(const void* ptrSrc, uint32_t strideSrc, void* ptrDest, uint32_t strideDest)
	{
		HRESULT hr;
		D3D11_MAPPED_SUBRESOURCE mappedInput; ZeroMemory(&mappedInput, sizeof(mappedInput));
		hr = this->pDeviceContext->Map(inputBuffer.Get(), 0, D3D11_MAP_WRITE, 0, &mappedInput);

		uint32_t lengthLineInputBuffer = this->CalcSizeOfSourceLine(this->width);
		for (uint32_t y = 0; y < this->height; ++y)
		{
			const float* ptr_Src = (const float*)(((char*)ptrSrc) + strideSrc*y);
			float* ptr_Dest = (float*)(((char*)mappedInput.pData) + lengthLineInputBuffer *y);
			memcpy(ptr_Dest, ptr_Src, lengthLineInputBuffer);
		}

		this->pDeviceContext->Unmap(inputBuffer.Get(), 0);

		ComPtr<ID3D11UnorderedAccessView> pUAVResult;
		hr = this->DoTransform(inputUAV.Get(), pUAVResult.GetAddressOf());
		//hr = this->pFFT->ForwardTransform(inputUAV.Get(), pUAVResult.GetAddressOf());

		ComPtr<ID3D11Resource> pResultResource;
		pUAVResult->GetResource(pResultResource.GetAddressOf());

		D3D11_MAPPED_SUBRESOURCE mappedOutput; ZeroMemory(&mappedOutput, sizeof(mappedOutput));
		hr = this->pDeviceContext->Map(pResultResource.Get(), 0, D3D11_MAP_READ, 0, &mappedOutput);

		for (uint32_t y = 0; y < this->height; ++y)
		{
			const float* ptr_Src = (const float*)(((char*)mappedOutput.pData) + this->width * 2 * sizeof(float)*y);
			float* ptr_Dest = (float*)(((char*)ptrDest) + strideDest*y);
			memcpy(ptr_Dest, ptr_Src, this->width*sizeof(float) * 2);
		}

		this->pDeviceContext->Unmap(pResultResource.Get(), 0);
	}

protected:
	float CalcFFTFactor(double normalizationFactor)
	{
		float f;
		if (IsNormalizationFactorDivBySize(normalizationFactor))
		{
			f = (float)(this->width*this->height);
		}
		else if (IsNormalizationFactorDivBySqrtSize(normalizationFactor))
		{
			f = sqrtf((float)(this->width*this->height));
		}
		else
		{
			f = (float)normalizationFactor;
		}

		return (float)(1.0f / f);
	}

	std::pair<ComPtr<ID3D11Device>, ComPtr<ID3D11DeviceContext>> CreateDevice()
	{
		ComPtr<ID3D11Device> pDevice;
		ComPtr<ID3D11DeviceContext> pDeviceContext;
		D3D_FEATURE_LEVEL featureLevel = (D3D_FEATURE_LEVEL)0;
		HRESULT hr = D3D11CreateDevice(
			NULL,
			D3D_DRIVER_TYPE_HARDWARE,
			NULL,
			D3D11_CREATE_DEVICE_DISABLE_GPU_TIMEOUT,
			NULL,
			0,
			D3D11_SDK_VERSION,
			&pDevice,
			&featureLevel,
			&pDeviceContext);
		if (FAILED(hr))
		{
			throw std::invalid_argument("couldn't create device");
		}

		return std::make_pair(pDevice, pDeviceContext);
	}

	bool SetupBuffers(ID3DX11FFT* pFFT, const D3DX11_FFT_BUFFER_INFO& fftBufferInfo)
	{
		ID3D11Buffer* tempBuffers[D3DX11_FFT_MAX_TEMP_BUFFERS] = { 0 };
		ID3D11Buffer* preComputeBuffers[D3DX11_FFT_MAX_PRECOMPUTE_BUFFERS] = { 0 };

		HRESULT hr;
		for (UINT i = 0; i < fftBufferInfo.NumTempBufferSizes; ++i)
		{
			hr = this->Create_Raw_Buffer(fftBufferInfo.TempBufferFloatSizes[i], &(tempBuffers[i]), D3D11_CPU_ACCESS_READ);
			if (FAILED(hr))
			{
				ReleaseBuffersEtc(tempBuffers, D3DX11_FFT_MAX_TEMP_BUFFERS, nullptr, 0, nullptr, 0, nullptr, 0);
				return false;
			}
		}

		for (UINT i = 0; i < fftBufferInfo.NumPrecomputeBufferSizes; ++i)
		{
			hr = this->Create_Raw_Buffer(fftBufferInfo.PrecomputeBufferFloatSizes[i], &(preComputeBuffers[i]));
			if (FAILED(hr))
			{
				ReleaseBuffersEtc(tempBuffers, D3DX11_FFT_MAX_TEMP_BUFFERS, preComputeBuffers, D3DX11_FFT_MAX_PRECOMPUTE_BUFFERS, nullptr, 0, nullptr, 0);
				return false;
			}
		}

		ID3D11UnorderedAccessView* pUAVTempBuffers[D3DX11_FFT_MAX_TEMP_BUFFERS] = { 0 };
		ID3D11UnorderedAccessView* pUAVPreComputerBuffers[D3DX11_FFT_MAX_PRECOMPUTE_BUFFERS] = { 0 };
		for (UINT i = 0; i < fftBufferInfo.NumTempBufferSizes; ++i)
		{
			hr = this->CreateUnorderedAccessView(fftBufferInfo.TempBufferFloatSizes[i], tempBuffers[i], &(pUAVTempBuffers[i]));
			if (FAILED(hr))
			{
				ReleaseBuffersEtc(tempBuffers, D3DX11_FFT_MAX_TEMP_BUFFERS, preComputeBuffers, D3DX11_FFT_MAX_PRECOMPUTE_BUFFERS, pUAVTempBuffers, D3DX11_FFT_MAX_TEMP_BUFFERS, nullptr, 0);
				return false;
			}
		}

		for (UINT i = 0; i < fftBufferInfo.NumPrecomputeBufferSizes; ++i)
		{
			hr = this->CreateUnorderedAccessView(fftBufferInfo.PrecomputeBufferFloatSizes[i], preComputeBuffers[i], &(pUAVPreComputerBuffers[i]));
			if (FAILED(hr))
			{
				ReleaseBuffersEtc(tempBuffers, D3DX11_FFT_MAX_TEMP_BUFFERS, preComputeBuffers, D3DX11_FFT_MAX_PRECOMPUTE_BUFFERS, pUAVTempBuffers, D3DX11_FFT_MAX_TEMP_BUFFERS, pUAVPreComputerBuffers, D3DX11_FFT_MAX_PRECOMPUTE_BUFFERS);
				return false;
			}
		}

		hr = pFFT->AttachBuffersAndPrecompute(fftBufferInfo.NumTempBufferSizes, pUAVTempBuffers, fftBufferInfo.NumPrecomputeBufferSizes, pUAVPreComputerBuffers);

		ReleaseBuffersEtc(tempBuffers, D3DX11_FFT_MAX_TEMP_BUFFERS, preComputeBuffers, D3DX11_FFT_MAX_PRECOMPUTE_BUFFERS, pUAVTempBuffers, D3DX11_FFT_MAX_TEMP_BUFFERS, pUAVPreComputerBuffers, D3DX11_FFT_MAX_PRECOMPUTE_BUFFERS);

		return (SUCCEEDED(hr)) ? true : false;
	}

	static void ReleaseBuffersEtc(ID3D11Buffer** ppBuffers1, int countBuffers1, ID3D11Buffer** ppBuffers2, int countBuffers2, ID3D11UnorderedAccessView** ppUAV1, int countUAV1, ID3D11UnorderedAccessView** ppUAV2, int countUAV2)
	{
		for (int i = 0; i < countBuffers1; ++i)
		{
			if (ppBuffers1[i] != nullptr)
			{
				ppBuffers1[i]->Release();
			}
		}

		for (int i = 0; i < countBuffers2; ++i)
		{
			if (ppBuffers2[i] != nullptr)
			{
				ppBuffers2[i]->Release();
			}
		}

		for (int i = 0; i < countUAV1; ++i)
		{
			if (ppUAV1[i] != nullptr)
			{
				ppUAV1[i]->Release();
			}
		}

		for (int i = 0; i < countUAV2; ++i)
		{
			if (ppUAV2[i] != nullptr)
			{
				ppUAV2[i]->Release();
			}
		}
	}

	HRESULT CreateUnorderedAccessView(UINT floatSize, ID3D11Buffer *pBuffer, ID3D11UnorderedAccessView **ppUAV) const
	{
		D3D11_UNORDERED_ACCESS_VIEW_DESC UAVDesc;
		ZeroMemory(&UAVDesc, sizeof(UAVDesc));
		UAVDesc.Format = DXGI_FORMAT_R32_TYPELESS;
		UAVDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
		UAVDesc.Buffer.FirstElement = 0;
		UAVDesc.Buffer.NumElements = floatSize;
		UAVDesc.Buffer.Flags = D3D11_BUFFER_UAV_FLAG_RAW;

		return this->pDevice->CreateUnorderedAccessView(pBuffer, &UAVDesc, ppUAV);
	}

	HRESULT Create_Raw_Buffer(UINT floatSize, ID3D11Buffer **ppBuffer, UINT cpuAccessFlags = 0) const
	{
		D3D11_BUFFER_DESC desc;
		ZeroMemory(&desc, sizeof(desc));
		desc.BindFlags = D3D11_BIND_UNORDERED_ACCESS;
		desc.ByteWidth = floatSize * sizeof(float);
		desc.CPUAccessFlags = cpuAccessFlags;
		desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS;
		desc.Usage = D3D11_USAGE_DEFAULT;

		return this->pDevice->CreateBuffer(&desc, NULL, ppBuffer);
	}


	virtual std::pair < ComPtr < ID3DX11FFT >, D3DX11_FFT_BUFFER_INFO> CreateFFT(double normalizationFactor) = 0;

	virtual UINT CalcInputBufferSize(uint32_t width, uint32_t height) = 0;

	virtual uint32_t CalcSizeOfSourceLine(uint32_t width) = 0;

	virtual HRESULT DoTransform(const ID3D11UnorderedAccessView *pInputBuffer, ID3D11UnorderedAccessView **ppOutputBuffer) = 0;
};

class CRealToComplex_ComplexD3DCSXFft_2 : protected CD3DCSX11Base, public IRealToComplex_Complex
{
public:
	CRealToComplex_ComplexD3DCSXFft_2(uint32_t width, uint32_t height, double normalizationFactor) : CD3DCSX11Base(width, height){ this->Initialize(normalizationFactor); }
	virtual void RealToComplex_Complex(const FourierTransfRealInputData2d& input, const FourierTransfComplexOutput2d& output, const FourierTransformAdditional* pAdditional)
	{
		HRESULT hr;
		D3D11_MAPPED_SUBRESOURCE mappedInput; ZeroMemory(&mappedInput, sizeof(mappedInput));
		hr = this->pDeviceContext->Map(inputBuffer.Get(), 0, D3D11_MAP_WRITE, 0, &mappedInput);

		for (uint32_t y = 0; y < input.height; ++y)
		{
			const float* ptrSrc = (const float*)(((char*)input.ptrData) + input.pitch*y);
			float* ptrDest = (float*)(((char*)mappedInput.pData) + input.width*sizeof(float)*y);
			memcpy(ptrDest, ptrSrc, input.width*sizeof(float));
		}

		this->pDeviceContext->Unmap(inputBuffer.Get(), 0);

		ComPtr<ID3D11UnorderedAccessView> pUAVResult;
		hr = this->pFFT->ForwardTransform(inputUAV.Get(), pUAVResult.GetAddressOf());

		ComPtr<ID3D11Resource> pResultResource;
		pUAVResult->GetResource(pResultResource.GetAddressOf());

		D3D11_MAPPED_SUBRESOURCE mappedOutput; ZeroMemory(&mappedOutput, sizeof(mappedOutput));
		hr = this->pDeviceContext->Map(pResultResource.Get(), 0, D3D11_MAP_READ, 0, &mappedOutput);

		for (uint32_t y = 0; y < input.height; ++y)
		{
			const float* ptrSrc = (const float*)(((char*)mappedOutput.pData) + output.width * 2 * sizeof(float)*y);
			float* ptrDest = (float*)(((char*)output.ptrData) + output.pitch*y);
			memcpy(ptrDest, ptrSrc, input.width*sizeof(float) * 2);
		}

		this->pDeviceContext->Unmap(pResultResource.Get(), 0);
	}

protected:
	virtual std::pair < ComPtr < ID3DX11FFT >, D3DX11_FFT_BUFFER_INFO> CreateFFT(double normalizationFactor)
	{
		ComPtr < ID3DX11FFT > fft;
		D3DX11_FFT_BUFFER_INFO fftBufferInfo; ZeroMemory(&fftBufferInfo, sizeof(fftBufferInfo));
		HRESULT hr = D3DX11CreateFFT2DReal(this->pDeviceContext.Get(), this->width, this->height, 0, &fftBufferInfo, &fft);
		hr = fft->SetForwardScale(this->CalcFFTFactor(normalizationFactor));
		return std::make_pair(fft, fftBufferInfo);
	}

	virtual UINT CalcInputBufferSize(uint32_t width, uint32_t height)
	{
		return width*height;
	}

	virtual uint32_t CalcSizeOfSourceLine(uint32_t width)
	{
		return width * sizeof(float);
	}

	virtual HRESULT DoTransform(const ID3D11UnorderedAccessView* pInputBuffer, ID3D11UnorderedAccessView** ppOutputBuffer)
	{
		return this->pFFT->ForwardTransform(pInputBuffer, ppOutputBuffer);
	}
};

class CComplexToComplex_ComplexD3DCSXFft_2_Base : public CD3DCSX11Base
{
public:
	CComplexToComplex_ComplexD3DCSXFft_2_Base(uint32_t width, uint32_t height)
		: CD3DCSX11Base(width, height)
	{}

protected:
	virtual std::pair < ComPtr < ID3DX11FFT >, D3DX11_FFT_BUFFER_INFO> CreateFFT(double normalizationFactor)
	{
		ComPtr < ID3DX11FFT > fft;
		D3DX11_FFT_BUFFER_INFO fftBufferInfo; ZeroMemory(&fftBufferInfo, sizeof(fftBufferInfo));
		HRESULT hr = D3DX11CreateFFT2DComplex(this->pDeviceContext.Get(), this->width, this->height, 0, &fftBufferInfo, &fft);
		this->SetNormalizationFactor(fft.Get(), this->CalcFFTFactor(normalizationFactor));
		return std::make_pair(fft, fftBufferInfo);
	}

	virtual UINT CalcInputBufferSize(uint32_t width, uint32_t height)
	{
		return width*height * 2;
	}

	virtual uint32_t CalcSizeOfSourceLine(uint32_t width)
	{
		return width * sizeof(float) * 2;
	}

	virtual void SetNormalizationFactor(ID3DX11FFT* pFFT, float normalizationFactor) = 0;
};


class CComplexToComplexFwd_ComplexD3DCSXFft_2 : protected CComplexToComplex_ComplexD3DCSXFft_2_Base, public IComplexToComplexFwd_Complex
{
public:
	CComplexToComplexFwd_ComplexD3DCSXFft_2(uint32_t width, uint32_t height, double normalizationFactor) : CComplexToComplex_ComplexD3DCSXFft_2_Base(width, height)
	{
		this->Initialize(normalizationFactor);
	}

	virtual void ComplexToComplex_Complex(const FourierTransfComplexInput2d& input, const FourierTransfComplexOutput2d& output, const FourierTransformAdditional* pAdditional)
	{
		this->Do_Transform(input.ptrData, input.pitch, output.ptrData, output.pitch);
	}

protected:
	virtual void SetNormalizationFactor(ID3DX11FFT* pFFT, float normalizationFactor)
	{
		HRESULT hr = pFFT->SetForwardScale(normalizationFactor);
	}

	virtual HRESULT DoTransform(const ID3D11UnorderedAccessView* pInputBuffer, ID3D11UnorderedAccessView** ppOutputBuffer)
	{
		return this->pFFT->ForwardTransform(pInputBuffer, ppOutputBuffer);
	}
};

class CComplexToComplexInv_ComplexD3DCSXFft_2 : protected CComplexToComplex_ComplexD3DCSXFft_2_Base, public IComplexToComplexInv_Complex
{
public:
	CComplexToComplexInv_ComplexD3DCSXFft_2(uint32_t width, uint32_t height, double normalizationFactor)
		: CComplexToComplex_ComplexD3DCSXFft_2_Base(width, height)
	{
		this->Initialize(normalizationFactor);
	}

	virtual void ComplexToComplex_Complex(const FourierTransfComplexInput2d& input, const FourierTransfComplexOutput2d& output, const FourierTransformAdditional* pAdditional)
	{
		this->Do_Transform(input.ptrData, input.pitch, output.ptrData, output.pitch);
	}
protected:
	virtual void SetNormalizationFactor(ID3DX11FFT* pFFT, float normalizationFactor)
	{
		HRESULT hr = pFFT->SetInverseScale(normalizationFactor);
	}

	virtual HRESULT DoTransform(const ID3D11UnorderedAccessView* pInputBuffer, ID3D11UnorderedAccessView** ppOutputBuffer)
	{
		return this->pFFT->InverseTransform(pInputBuffer, ppOutputBuffer);
	}
};

class CComplexHermetianToReal_ComplexD3DCSXFft_2 :protected CComplexToComplex_ComplexD3DCSXFft_2_Base, public IComplexHermitianToReal
{
public:
	CComplexHermetianToReal_ComplexD3DCSXFft_2(uint32_t width, uint32_t height, double normalizationFactor)
		: CComplexToComplex_ComplexD3DCSXFft_2_Base(width, height)
	{
		this->Initialize(normalizationFactor);
	}

	virtual void ComplexHermitianToReal(const FourierTransfComplexInput2d& input, const FourierTransfRealOutputData2d& output, const FourierTransformAdditional* pAdditional)
	{
		this->Do_Transform(input.ptrData, input.pitch, output.ptrData, output.pitch);
	}
protected:
	virtual void SetNormalizationFactor(ID3DX11FFT* pFFT, float normalizationFactor)
	{
		HRESULT hr = pFFT->SetInverseScale(normalizationFactor);
	}

	void Do_Transform(const void* ptrSrc, uint32_t strideSrc, void* ptrDest, uint32_t strideDest)
	{
		HRESULT hr;
		D3D11_MAPPED_SUBRESOURCE mappedInput; ZeroMemory(&mappedInput, sizeof(mappedInput));
		hr = this->pDeviceContext->Map(inputBuffer.Get(), 0, D3D11_MAP_WRITE, 0, &mappedInput);

		uint32_t lengthLineInputBuffer = this->CalcSizeOfSourceLine(this->width);
		for (uint32_t y = 0; y < this->height; ++y)
		{
			const float* ptr_Src = (const float*)(((char*)ptrSrc) + strideSrc*y);
			float* ptr_Dest = (float*)(((char*)mappedInput.pData) + lengthLineInputBuffer *y);
			memcpy(ptr_Dest, ptr_Src, lengthLineInputBuffer);
		}

		this->pDeviceContext->Unmap(inputBuffer.Get(), 0);

		ComPtr<ID3D11UnorderedAccessView> pUAVResult;
		hr = this->pFFT->InverseTransform(inputUAV.Get(), pUAVResult.GetAddressOf());

		ComPtr<ID3D11Resource> pResultResource;
		pUAVResult->GetResource(pResultResource.GetAddressOf());

		D3D11_MAPPED_SUBRESOURCE mappedOutput; ZeroMemory(&mappedOutput, sizeof(mappedOutput));
		hr = this->pDeviceContext->Map(pResultResource.Get(), 0, D3D11_MAP_READ, 0, &mappedOutput);

		for (uint32_t y = 0; y < this->height; ++y)
		{
			const float* ptr_Src = (const float*)(((char*)mappedOutput.pData) + this->width * 2 * sizeof(float)*y);
			float* ptr_Dest = (float*)(((char*)ptrDest) + strideDest*y);
			for (uint32_t x = 0; x < this->width; ++x)
			{
				ptr_Dest[x] = ptr_Src[2 * x];
			}
			///memcpy(ptr_Dest, ptr_Src, this->width*sizeof(float) * 2);
		}

		this->pDeviceContext->Unmap(pResultResource.Get(), 0);
	}

	virtual HRESULT DoTransform(const ID3D11UnorderedAccessView* pInputBuffer, ID3D11UnorderedAccessView** ppOutputBuffer)
	{
		return E_NOTIMPL;
	}
};

class CRealToComplex_ComplexD3DCSXFft : public IRealToComplex_Complex
{
protected:
	uint32_t width, height;

	ComPtr<ID3D11Device> pDevice;
	ComPtr<ID3D11DeviceContext> pDeviceContext;
	ComPtr<ID3DX11FFT> pFFT;
	ComPtr<ID3D11Buffer> inputBuffer;
	ComPtr<ID3D11UnorderedAccessView> inputUAV;
public:
	CRealToComplex_ComplexD3DCSXFft(uint32_t width, uint32_t height) : width(width), height(height)
	{
		D3D_FEATURE_LEVEL featureLevel = (D3D_FEATURE_LEVEL)0;
		HRESULT hr = D3D11CreateDevice(
			NULL,
			D3D_DRIVER_TYPE_HARDWARE,
			NULL,
			D3D11_CREATE_DEVICE_DISABLE_GPU_TIMEOUT,
			NULL,
			0,
			D3D11_SDK_VERSION,
			&this->pDevice,
			&featureLevel,
			&(this->pDeviceContext));

		D3DX11_FFT_BUFFER_INFO fftBufferInfo; ZeroMemory(&fftBufferInfo, sizeof(fftBufferInfo));
		hr = D3DX11CreateFFT2DReal(
			this->pDeviceContext.Get(),
			width,
			height,
			0,
			&fftBufferInfo,
			&(this->pFFT));

		this->SetupBuffers(this->pFFT.Get(), fftBufferInfo);

		hr = Create_Raw_Buffer(width*height, this->inputBuffer.GetAddressOf(), D3D11_CPU_ACCESS_WRITE);
		hr = CreateUnorderedAccessView(width*height, this->inputBuffer.Get(), this->inputUAV.GetAddressOf());
	}

	~CRealToComplex_ComplexD3DCSXFft()
	{}

	virtual void RealToComplex_Complex(const FourierTransfRealInputData2d& input, const FourierTransfComplexOutput2d& output, const FourierTransformAdditional* pAdditional)
	{
		HRESULT hr;
		D3D11_MAPPED_SUBRESOURCE mappedInput; ZeroMemory(&mappedInput, sizeof(mappedInput));
		hr = this->pDeviceContext->Map(inputBuffer.Get(), 0, D3D11_MAP_WRITE, 0, &mappedInput);

		for (uint32_t y = 0; y < input.height; ++y)
		{
			const float* ptrSrc = (const float*)(((char*)input.ptrData) + input.pitch*y);
			float* ptrDest = (float*)(((char*)mappedInput.pData) + input.width*sizeof(float)*y);
			memcpy(ptrDest, ptrSrc, input.width*sizeof(float));
		}

		this->pDeviceContext->Unmap(inputBuffer.Get(), 0);

		ComPtr<ID3D11UnorderedAccessView> pUAVResult;
		hr = this->pFFT->ForwardTransform(inputUAV.Get(), pUAVResult.GetAddressOf());

		ComPtr<ID3D11Resource> pResultResource;
		pUAVResult->GetResource(pResultResource.GetAddressOf());

		D3D11_MAPPED_SUBRESOURCE mappedOutput; ZeroMemory(&mappedOutput, sizeof(mappedOutput));
		hr = this->pDeviceContext->Map(pResultResource.Get(), 0, D3D11_MAP_READ, 0, &mappedOutput);

		for (uint32_t y = 0; y < input.height; ++y)
		{
			const float* ptrSrc = (const float*)(((char*)mappedOutput.pData) + output.width * 2 * sizeof(float)*y);
			float* ptrDest = (float*)(((char*)output.ptrData) + output.pitch*y);
			memcpy(ptrDest, ptrSrc, input.width*sizeof(float) * 2);
		}

		this->pDeviceContext->Unmap(pResultResource.Get(), 0);
	}

protected:
	static void ReleaseBuffersEtc(ID3D11Buffer** ppBuffers1, int countBuffers1, ID3D11Buffer** ppBuffers2, int countBuffers2, ID3D11UnorderedAccessView** ppUAV1, int countUAV1, ID3D11UnorderedAccessView** ppUAV2, int countUAV2)
	{
		for (int i = 0; i < countBuffers1; ++i)
		{
			if (ppBuffers1[i] != nullptr)
			{
				ppBuffers1[i]->Release();
			}
		}

		for (int i = 0; i < countBuffers2; ++i)
		{
			if (ppBuffers2[i] != nullptr)
			{
				ppBuffers2[i]->Release();
			}
		}

		for (int i = 0; i < countUAV1; ++i)
		{
			if (ppUAV1[i] != nullptr)
			{
				ppUAV1[i]->Release();
			}
		}

		for (int i = 0; i < countUAV2; ++i)
		{
			if (ppUAV2[i] != nullptr)
			{
				ppUAV2[i]->Release();
			}
		}
	}

	bool SetupBuffers(ID3DX11FFT* pFFT, const D3DX11_FFT_BUFFER_INFO& fftBufferInfo)
	{
		ID3D11Buffer* tempBuffers[D3DX11_FFT_MAX_TEMP_BUFFERS] = { 0 };
		ID3D11Buffer* preComputeBuffers[D3DX11_FFT_MAX_PRECOMPUTE_BUFFERS] = { 0 };

		HRESULT hr;
		for (UINT i = 0; i < fftBufferInfo.NumTempBufferSizes; ++i)
		{
			hr = this->Create_Raw_Buffer(fftBufferInfo.TempBufferFloatSizes[i], &(tempBuffers[i]), D3D11_CPU_ACCESS_READ);
			if (FAILED(hr))
			{
				ReleaseBuffersEtc(tempBuffers, D3DX11_FFT_MAX_TEMP_BUFFERS, nullptr, 0, nullptr, 0, nullptr, 0);
				return false;
			}
		}

		for (UINT i = 0; i < fftBufferInfo.NumPrecomputeBufferSizes; ++i)
		{
			hr = this->Create_Raw_Buffer(fftBufferInfo.PrecomputeBufferFloatSizes[i], &(preComputeBuffers[i]));
			if (FAILED(hr))
			{
				ReleaseBuffersEtc(tempBuffers, D3DX11_FFT_MAX_TEMP_BUFFERS, preComputeBuffers, D3DX11_FFT_MAX_PRECOMPUTE_BUFFERS, nullptr, 0, nullptr, 0);
				return false;
			}
		}

		ID3D11UnorderedAccessView* pUAVTempBuffers[D3DX11_FFT_MAX_TEMP_BUFFERS] = { 0 };
		ID3D11UnorderedAccessView* pUAVPreComputerBuffers[D3DX11_FFT_MAX_PRECOMPUTE_BUFFERS] = { 0 };
		for (UINT i = 0; i < fftBufferInfo.NumTempBufferSizes; ++i)
		{
			hr = this->CreateUnorderedAccessView(fftBufferInfo.TempBufferFloatSizes[i], tempBuffers[i], &(pUAVTempBuffers[i]));
			if (FAILED(hr))
			{
				ReleaseBuffersEtc(tempBuffers, D3DX11_FFT_MAX_TEMP_BUFFERS, preComputeBuffers, D3DX11_FFT_MAX_PRECOMPUTE_BUFFERS, pUAVTempBuffers, D3DX11_FFT_MAX_TEMP_BUFFERS, nullptr, 0);
				return false;
			}
		}

		for (UINT i = 0; i < fftBufferInfo.NumPrecomputeBufferSizes; ++i)
		{
			hr = this->CreateUnorderedAccessView(fftBufferInfo.PrecomputeBufferFloatSizes[i], preComputeBuffers[i], &(pUAVPreComputerBuffers[i]));
			if (FAILED(hr))
			{
				ReleaseBuffersEtc(tempBuffers, D3DX11_FFT_MAX_TEMP_BUFFERS, preComputeBuffers, D3DX11_FFT_MAX_PRECOMPUTE_BUFFERS, pUAVTempBuffers, D3DX11_FFT_MAX_TEMP_BUFFERS, pUAVPreComputerBuffers, D3DX11_FFT_MAX_PRECOMPUTE_BUFFERS);
				return false;
			}
		}

		hr = this->pFFT->AttachBuffersAndPrecompute(fftBufferInfo.NumTempBufferSizes, pUAVTempBuffers, fftBufferInfo.NumPrecomputeBufferSizes, pUAVPreComputerBuffers);

		ReleaseBuffersEtc(tempBuffers, D3DX11_FFT_MAX_TEMP_BUFFERS, preComputeBuffers, D3DX11_FFT_MAX_PRECOMPUTE_BUFFERS, pUAVTempBuffers, D3DX11_FFT_MAX_TEMP_BUFFERS, pUAVPreComputerBuffers, D3DX11_FFT_MAX_PRECOMPUTE_BUFFERS);

		return (SUCCEEDED(hr)) ? true : false;
	}

	HRESULT CreateUnorderedAccessView(UINT floatSize, ID3D11Buffer *pBuffer, ID3D11UnorderedAccessView **ppUAV) const
	{
		D3D11_UNORDERED_ACCESS_VIEW_DESC UAVDesc;
		ZeroMemory(&UAVDesc, sizeof(UAVDesc));
		UAVDesc.Format = DXGI_FORMAT_R32_TYPELESS;
		UAVDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
		UAVDesc.Buffer.FirstElement = 0;
		UAVDesc.Buffer.NumElements = floatSize;
		UAVDesc.Buffer.Flags = D3D11_BUFFER_UAV_FLAG_RAW;

		return this->pDevice->CreateUnorderedAccessView(pBuffer, &UAVDesc, ppUAV);
	}

	HRESULT Create_Raw_Buffer(UINT floatSize, ID3D11Buffer **ppBuffer, UINT cpuAccessFlags = 0) const
	{
		D3D11_BUFFER_DESC desc;
		ZeroMemory(&desc, sizeof(desc));
		desc.BindFlags = D3D11_BIND_UNORDERED_ACCESS;
		desc.ByteWidth = floatSize * sizeof(float);
		desc.CPUAccessFlags = cpuAccessFlags;
		desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS;
		desc.Usage = D3D11_USAGE_DEFAULT;

		return this->pDevice->CreateBuffer(&desc, NULL, ppBuffer);
	}
};

//------------------------------------------------------------------------------

/*static*/ void CFourierTransformFactoryD3DCSXFft::GetVersionString(std::wstring& str)
{
	std::wstringstream ss;
	ss << R"(dll=")" << D3DCSX_DLL_W << R"(")";
	str = ss.str();
}

/*virtual*/std::shared_ptr<IRealToComplex_PackedFftwComplex> CFourierTransformFactoryD3DCSXFft::CreateRealToComplexPackedFftw(const Options& opts)
{
	return std::shared_ptr<IRealToComplex_PackedFftwComplex>();
}

/*virtual*/std::shared_ptr<IRealToComplex_PackedIppComplex> CFourierTransformFactoryD3DCSXFft::CreateRealToComplexPackedIpp(const Options& opts)
{
	return std::shared_ptr<IRealToComplex_PackedIppComplex>();
}

/*virtual*/std::shared_ptr<IRealToComplex_Complex> CFourierTransformFactoryD3DCSXFft::CreateRealToComplex(const Options& opts)
{
	return std::make_shared<CRealToComplex_ComplexD3DCSXFft_2>(opts.width, opts.height, opts.normalizationFactor);
}

/*virtual*/std::shared_ptr<IRealToComplex_Complex> CFourierTransformFactoryD3DCSXFft::CreateRealToComplex_Shifted(const Options& opts)
{
	return std::shared_ptr<IRealToComplex_Complex>();
}

/*virtual*/std::shared_ptr<IComplexToComplexFwd_Complex> CFourierTransformFactoryD3DCSXFft::CreateComplexToComplexFwd(const Options& opts)
{
	return std::make_shared<CComplexToComplexFwd_ComplexD3DCSXFft_2>(opts.width, opts.height, opts.normalizationFactor);
}

/*virtual*/std::shared_ptr<IComplexToComplexInv_Complex> CFourierTransformFactoryD3DCSXFft::CreateComplexToComplexInv(const Options& opts)
{
	return std::make_shared<CComplexToComplexInv_ComplexD3DCSXFft_2>(opts.width, opts.height, opts.normalizationFactor);
}

/*virtual*/std::shared_ptr<FTLib::IComplexHermitianToReal> CFourierTransformFactoryD3DCSXFft::CreateComplexHermitianToReal(const Options& opts)
{
	return std::make_shared<CComplexHermetianToReal_ComplexD3DCSXFft_2>(opts.width, opts.height, opts.normalizationFactor);
}